开发环境:
RT-Thread版本:4.0.3
操作系统:Windows 10
RT-Thread Studio版本:2.0.0
开发板MCU:AB5301A
2.1全彩LED简介
大家常的LED都是单色的,控制一端的高低电平即可控制LED的亮灭。全彩LED就是可以通过RGB三种基本LED组合多种色彩的LED,当然还有一种RGBW 四基色的全彩LED。全彩LED分为共阳极和共阴极两种。蓝讯AB32VG1的板子是共阳的。
全彩LED的控制有两种方式:一种是直接通过三个引脚直接控制,另外一种是通过SM16703、WS2811、TM1829等驱动芯片进行控制。本文采用直接控制的方式,但要想获得更多颜色,就需要通过PWM来控制。
2.2简单实现七彩色
2.2.1实验分析
先来看看电路图。
这些 LED 灯的阴极都是连接到 AB5301A的 GPIO 引脚,只要我们控制 GPIO 引脚的电平输出状态,即可控制 LED 灯的亮灭。
2.2.2代码实现
关于工程创建,这里就不说了,不懂的上上一章吧。
七色很简单,就通过控制三个引脚亮灭来组合其中颜色。直接看代码。
/**Includes*********************************************************************/
#include <rtthread.h>
#include "board.h"
/**define***********************************************************************/
#define LED_R "PE.1"
#define LED_G "PE.4"
#define LED_B "PA.2"
/** the macro definition to the led on or off
* 1 - off
*0 - on
*/
#define ON PIN_LOW
#define OFF PIN_HIGH
/**
* @brief main
* @param None
* @retval None
*/
int main(void)
{
uint32_t cnt = 0;
uint8_t pin_r = rt_pin_get(LED_R);
uint8_t pin_g = rt_pin_get(LED_G);
uint8_t pin_b = rt_pin_get(LED_B);
rt_pin_mode(pin_r, PIN_MODE_OUTPUT);
rt_pin_mode(pin_g, PIN_MODE_OUTPUT);
rt_pin_mode(pin_b, PIN_MODE_OUTPUT);
while (1)
{
//红
rt_pin_write(pin_r, ON);
rt_pin_write(pin_g, OFF);
rt_pin_write(pin_b, OFF);
rt_thread_mdelay(1000);
//绿
rt_pin_write(pin_r, OFF);
rt_pin_write(pin_g, ON);
rt_pin_write(pin_b, OFF);
rt_thread_mdelay(1000);
//蓝
rt_pin_write(pin_r, OFF);
rt_pin_write(pin_g, OFF);
rt_pin_write(pin_b, ON);
rt_thread_mdelay(1000);
//黄(红+绿)
rt_pin_write(pin_r, ON);
rt_pin_write(pin_g, ON);
rt_pin_write(pin_b, OFF);
rt_thread_mdelay(1000);
//紫(红+蓝)
rt_pin_write(pin_r, ON);
rt_pin_write(pin_g, OFF);
rt_pin_write(pin_b, ON);
rt_thread_mdelay(1000);
//青(绿+蓝)
rt_pin_write(pin_r, OFF);
rt_pin_write(pin_g, ON);
rt_pin_write(pin_b, ON);
rt_thread_mdelay(1000);
//白(红+绿+蓝)
rt_pin_write(pin_r, ON);
rt_pin_write(pin_g, ON);
rt_pin_write(pin_b, ON);
rt_thread_mdelay(1000);
//黑(全部关闭)
rt_pin_write(pin_r, OFF);
rt_pin_write(pin_g, OFF);
rt_pin_write(pin_b, OFF);
rt_thread_mdelay(1000);
cnt++;
}
return 0;
}
代码很简单,主要是通过rt_pin_write()
函数来控制不同LED。
2.2.3实验现象
编译完成后,通过Downloader
下载程序,即可看到不同七色光依次出现。
2.3 PWM 控制全彩LED
2.3.1实验分析
在正式讲解之前,先来说说什么是PWM。PWM(Pulse Width Modulation , 脉冲宽度调制) 是一种对模拟信号电平进行数字编码的方法,通过不同频率的脉冲使用方波的占空比用来对一个具体模拟信号的电平进行编码,使输出端得到一系列幅值相等的脉冲,用这些脉冲来代替所需要波形的设备。
如上图所示,就是PWM 原理示意图,假定定时器工作模式为向上计数,当计数值小于阈值时,则输出一种电平状态,比如高电平,当计数值大于阈值时则输出相反的电平状态,比如低电平。当计数值达到最大值是,计数器从0开始重新计数,又回到最初的电平状态。高电平持续时间(脉冲宽度)和周期时间的比值就是占空比,范围为0~100%。上图高电平的持续时间刚好是周期时间的一半,所以占空比为50%。
上一节讲了控制三基色的亮灭实现了七色,这一节通过使用 PWM对R、G、B三个灯进行颜色控制,通过改变三个通道的占空比,三个灯进行颜色的组合,就可以混合成多种不同的颜色。
那么要怎么做呢?其实很简单,也就通过改变电平的占空比使RGB分别显示出不同的色值,然后通过组合显示出想要的颜色。通过PWM和直接控制三基色有啥区别呢,其实没区别,通过PWM控制的颗粒度更细,因此能得到更多的色彩。
值得注意的是,这里要调整LED_R的GPIO,将LED_R接到PA1上,便于后面的控制。
Name | Type | Function |
---|---|---|
PE4(LED_G) | I/O | SPI0DI-G2 SPI1DI-G6 LPWM0-G1 IISMCLK-G2PE4 |
PA1(LED_R) | I/O | SPDIF1 SPI1CLK-G1 TX0-G5 HSTRX-G5 LPWM1-G3 IISDO-G1 PA1 |
PA2(LED_B) | I/O | SPI1DI-G1 LPWM2-G3 IISSCLK-G1 PA2 |
2.2.3代码实现
首先我们需要配置PWM驱动,双击RT-Thread Settings文件,打开 RT-Thread 项目配置界面,勾选PWM驱动,然后保存即可。
接下来需要使能3个LED的PWM。
还可以通过env工具来配置。
也可直接在rtconfig.h
中直接添加以下宏定义即可。
如果以上添加成功,编译烧写到开发板,查看设别,可以看到控制3个LED的PWM设备。
接下来就是下应用代码,在applications
文件夹下新建文件,添加一个线程任务,然后在任务中操作3个LED。代码如下:
/**
******************************************************************************
* @file task.c
* @author BruceOu
* @version V1.0
* @date 2021-03-12
* @blog https://blog.bruceou.cn/
* @Official Accounts 嵌入式实验楼
* @brief RTT任务
******************************************************************************
*/
/*Includes**********************************************************************/
#include "task.h"
#define THREAD_PRIORITY 7
#define THREAD_STACK_SIZE 512
#define THREAD_TIMESLICE 3
#define PWM_DEV_NAME_R "lpwm1" /* PWM设备名称 */
#define PWM_DEV_CHANNEL_R 3 /* PWM通道 */
#define PWM_DEV_NAME_G "lpwm0" /* PWM设备名称 */
#define PWM_DEV_CHANNEL_G 1 /* PWM通道 */
#define PWM_DEV_NAME_B "lpwm2" /* PWM设备名称 */
#define PWM_DEV_CHANNEL_B 3 /* PWM通道 */
struct rt_device_pwm *pwm_dev_r; /* PWM设备句柄 */
struct rt_device_pwm *pwm_dev_g; /* PWM设备句柄 */
struct rt_device_pwm *pwm_dev_b; /* PWM设备句柄 */
static rt_thread_t pwm_led_tid = RT_NULL;
/* 线程 pwm_led_thread_entry 的入口函数 */
/**
* @brief pwm_led_thread_entry
* @param parameter
* @retval None
*/
static void pwm_led_thread_entry(void *parameter)
{
rt_uint32_t period, pulse_r,pulse_g,pulse_b, dir_r,dir_g,dir_b;
period = 655360; /* 周期为0.5ms,单位为纳秒ns */
dir_r = 1; /* PWM脉冲宽度值的增减方向 */
dir_g = 1;
dir_b = 1;
pulse_r = 0; /* PWM脉冲宽度值,单位为纳秒ns */
pulse_g = 0;
pulse_b = 0;
rt_uint16_t r,g,b;
/* 查找设备 */
pwm_dev_r = (struct rt_device_pwm *)rt_device_find(PWM_DEV_NAME_R);
if (pwm_dev_r == RT_NULL)
{
rt_kprintf("pwm led r run failed! can't find %s device!\n", PWM_DEV_NAME_G);
}
pwm_dev_g = (struct rt_device_pwm *)rt_device_find(PWM_DEV_NAME_G);
if (pwm_dev_g == RT_NULL)
{
rt_kprintf("pwm led g run failed! can't find %s device!\n", PWM_DEV_NAME_G);
}
pwm_dev_b = (struct rt_device_pwm *)rt_device_find(PWM_DEV_NAME_B);
if (pwm_dev_b == RT_NULL)
{
rt_kprintf("pwm led b run failed! can't find %s device!\n", PWM_DEV_NAME_B);
}
/* 设置PWM周期和脉冲宽度默认值 */
rt_pwm_set(pwm_dev_r, PWM_DEV_CHANNEL_R, period, pulse_r);
rt_pwm_set(pwm_dev_g, PWM_DEV_CHANNEL_G, period, pulse_g);
rt_pwm_set(pwm_dev_b, PWM_DEV_CHANNEL_B, period, pulse_b);
/* 使能设备 */
rt_pwm_enable(pwm_dev_r, PWM_DEV_CHANNEL_R);
rt_pwm_enable(pwm_dev_g, PWM_DEV_CHANNEL_G);
rt_pwm_enable(pwm_dev_b, PWM_DEV_CHANNEL_B);
while (1)
{
for (r =0 ; r < 8; r++)
{
if (dir_r)
{
pulse_r += 81920; /* 从0值开始每次增加81920ns */
}
else
{
pulse_r -= 81920; /* 从0值开始每次减少81920ns */
}
if ((pulse_r) >= period)
{
dir_r = 0;
}
if (81920 > pulse_r)
{
dir_r = 1;
}
/* 设置PWM周期和脉冲宽度 */
rt_pwm_set(pwm_dev_r, PWM_DEV_CHANNEL_R, period, pulse_r);
for(g = 0; g < 8; g++)
{
if (dir_g)
{
pulse_g += 81920; /* 从0值开始每次增加81920ns */
}
else
{
pulse_g -= 81920; /* 从0值开始每次减少81920ns */
}
if ((pulse_g) >= period)
{
dir_g = 0;
}
if (81920 > pulse_g)
{
dir_g = 1;
}
rt_pwm_set(pwm_dev_g, PWM_DEV_CHANNEL_G, period, pulse_g);
for(b = 0; b < 8; b++)
{
rt_thread_mdelay(10);
if (dir_b)
{
pulse_b += 81920; /* 从0值开始每次增加81920ns */
}
else
{
pulse_b -= 81920; /* 从0值开始每次减少81920ns */
}
if ((pulse_b) >= period)
{
dir_b = 0;
}
if (81920 > pulse_b)
{
dir_b = 1;
}
rt_pwm_set(pwm_dev_b, PWM_DEV_CHANNEL_B, period, pulse_b);
}
}
}
}
}
/* 线程初始化*/
int thread_init(void)
{
/* 创建线程,名称是 pwm_led_thread,入口是 pwm_led_thread*/
pwm_led_tid = rt_thread_create("pwm_led_thread",
pwm_led_thread_entry,
RT_NULL,
THREAD_STACK_SIZE,
THREAD_PRIORITY,
THREAD_TIMESLICE);
/* 如果获得线程控制块,启动这个线程 */
if (pwm_led_tid != RT_NULL)
rt_thread_startup(pwm_led_tid);
return 0;
}
/* 导出到 msh 命令列表中 */
MSH_CMD_EXPORT(thread_init, thread init);
当然啦以上代码还可以优化,有兴趣自行去修改吧。控制PWM主要用到了以下三个函数:
rt_device_find() //根据 PWM 设备名称查找设备获取设备句柄
rt_pwm_set() //设置 PWM 周期和脉冲宽度
rt_pwm_enable() //使能 PWM 设备
代码也很简单,就是通过PWM来控制三个LED的脉宽,从而得到不同的颜色。
2.3.3实验现象
烧写成功后复位,在终端输 thread_init
。
即可看到LED闪烁出不同的颜色。
当然啦,由于拍摄原因,呈现的效果不是很好。
我看到文档说3路LPWM互斥,楼主怎么还可以使用3路LPWM点亮LED呢